#include <iostream>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctime>

#define PROCESS_NUM 5
#define MakeSeed() srand(time(nullptr));

typedef void (*func_t)();//定义一个func_t的函数指针，返回值为void，没有参数

void downloadTask()
{
    std::cout << getpid() <<" :下载任务\n" << std::endl;
    sleep(1);
}

void ioTask()
{
    std::cout << getpid() <<" :IO任务\n" << std::endl;
    sleep(1);
}

void flushTask()
{
    std::cout << getpid() <<" :刷新任务\n" << std::endl;
    sleep(1);
}

void loadTaskFunc(std::vector<func_t> *out)
{
    assert(out);
    out->push_back(downloadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}

class subEndpoint
{
public:
    subEndpoint(pid_t subId, int writeFd)
    : _subId(subId), _writeFd(writeFd)
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", _num++, _subId, _writeFd);
        _name = nameBuffer;
    }

public:
    static int _num;//子进程编号
    std::string _name;//子进程对应的名称
    pid_t _subId;//子进程pid
    int _writeFd;//写端文件描述符
};
int subEndpoint::_num = 0;

int recvTask(int readFd)
{
    //强制读取4个字节
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof code);
    if (s == 4) return code;
    else return -1;
}

void createSubProcess(std::vector<subEndpoint> *subs, std::vector<func_t> &funcMap)
{

    for (int i = 0; i < PROCESS_NUM; i++)
    {
        //创建匿名管道
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;

        pid_t id = fork();
        if (id == 0)
        {
            //子进程关闭写端
            close(fds[1]);
            //执行任务
            while (true)
            {
                //获取命令码，没有则阻塞
                int commandCode = recvTask(fds[0]);
                //执行对应的任务
                if (commandCode >= 0 && (unsigned long)commandCode < funcMap.size())
                {
                    funcMap[commandCode]();
                }
                else if (commandCode == -1) break;
            }
            exit(0);
        }
        //父进程关闭读端
        close(fds[0]);
        //通过subEndpoint,将子进程的pid和父进程的写端建立关系，然后放进subs数组中。
        subEndpoint sub(id, fds[1]);
        subs->push_back(sub);
    }
}

void sendTask(const subEndpoint &process, int taskNum)
{
    std::cout << "send task num: " << taskNum << " send to -> " << process._name << std::endl;
    int n = write(process._writeFd, &taskNum, sizeof(taskNum));
    assert(n == sizeof(int));
    (void)n;//在release模式的时候，assert()不会被执行，所以n并不会被使用。这一行是为了使用n，消除warning
}

void loadBalnceControl(std::vector<subEndpoint> &subs, std::vector<func_t> &funcMap, int count)
{
    int processNum = subs.size();
    int taskNum = funcMap.size();
    bool forever = (count == 0 ? true : false);

    while (true)
    {
        //选择一个子进程
        int subIdx = rand() % processNum;
        //选择一个任务
        int taskIdx = rand() % taskNum;
        //将任务发送给指定的子进程
        sendTask(subs[subIdx], taskIdx);
        sleep(1);
        if (!forever)
        {
            count--;
            if (count == 0) break;
        }
    }
    //当父进程所有的任务被完成时，关闭写端，当子进程读到0的时候，就会被信号终止
    for (int i = 0; i < processNum; i++) close(subs[i]._writeFd);

}

void waitProcess(std::vector<subEndpoint> &subs)
{
    int processNum = subs.size();
    for (int i = 0; i < processNum; i++)
    {
        waitpid(subs[i]._subId, nullptr, 0);
        std::cout << "wait sub proecsss sucess ...: " << subs[i]._subId << std::endl;
    }
}

int main()
{
    MakeSeed();
    //加载任务表
    std::vector<func_t> funcMap;
    loadTaskFunc(&funcMap);

    //创建子进程，并建立好父子进程的通信管道
    std::vector<subEndpoint> subs;
    createSubProcess(&subs, funcMap);

    //父进程向子进程发送命令码，让每个子进程都能够执行到任务
    int taskCnt = 0;//执行任务的次数，0表示永远执行
    loadBalnceControl(subs, funcMap, taskCnt); 

    //回收子进程信息
    waitProcess(subs);

    return 0;
}
